#include "stdafx.h"
#include "wf_graphicview.h"

#include <qbitmap.h>
#include <qcursor.h>
#include <qlabel.h>
//Added by qt3to4:
#include <QKeyEvent>
#include <Q3GridLayout>
#include <QPixmap>
#include <QResizeEvent>
#include <QTabletEvent>
#include <QFocusEvent>
#include <QMouseEvent>
#include <QEvent>
#include <QPaintEvent>
#include <QWheelEvent>

#include "wf_zoomin.h"
#include "wf_zoompan.h"
#include "wf_zoomscroll.h"
#include "wf_modifydelete.h"
#include "wf_selectsingle.h"
#include "pf_graphic.h"
#include "pf_settings.h"
#include "pf_system.h"
#include "pf_painterqt.h"
#include "pf_patternlist.h"

#include "ui_cadtoolbar.h"

#include "wf_dialogfactory.h"
#include "qg_qt2rs.h"


#define QG_SCROLLMARGIN 400


/**
 * Constructor.
 */
WF_GraphicView::WF_GraphicView(QWidget* parent, const char* name, Qt::WFlags f)
        : QWidget(parent, name, f), PF_GraphicView() {

    setBackground(background);
    //coordinateWidget = NULL;
    //mouseWidget = NULL;
    //optionWidget = NULL;
    //cadToolBar = NULL;
    //commandWidget = NULL;
			
	redrawMethod=LX_Define::RedrawAll;
			
	PixmapLayer1=PixmapLayer2=PixmapLayer3=NULL;

    layout = new Q3GridLayout(this, 3, 2);
    layout->setColStretch(0, 1);
    layout->setColStretch(1, 0);
    layout->setColStretch(2, 0);
    layout->setRowStretch(0, 1);
    layout->setRowStretch(1, 0);

    hScrollBar = new UI_ScrollBar(Qt::Horizontal, this);
    hScrollBar->setLineStep(50);
    layout->addMultiCellWidget(hScrollBar, 1, 1, 0, 0);
    layout->addRowSpacing(1, hScrollBar->sizeHint().height());
    connect(hScrollBar, SIGNAL(valueChanged(int)),
            this, SLOT(slotHScrolled(int)));

    vScrollBar = new UI_ScrollBar(Qt::Vertical, this);
    vScrollBar->setLineStep(50);
    layout->addMultiCellWidget(vScrollBar, 0, 0, 2, 2);
    layout->addColSpacing(2, vScrollBar->sizeHint().width());
    connect(vScrollBar, SIGNAL(valueChanged(int)),
            this, SLOT(slotVScrolled(int)));

#ifndef __APPLE__
    // Mouse Cursors:
    QPixmap cur1(":ui/cur_cad_bmp.png");
    curCad = new QCursor(cur1, 15, 15);

    QPixmap cur2(":ui/cur_glass_bmp.png");
    curMagnifier = new QCursor(cur2, 12, 12);

    QPixmap cur3(":ui/cur_del_bmp.png");
    curDel = new QCursor(cur3, 15, 15);

    QPixmap cur4(":ui/cur_select_bmp.png");
    curSelect = new QCursor(cur4, 15, 15);

    QPixmap cur5(":ui/cur_hand_bmp.png");
    curHand = new QCursor(cur5, 15, 15);
#else
    // No individual cursors for the Mac
    curCad = NULL;
    curMagnifier = NULL;
    curDel = NULL;
    curSelect = NULL;
    curHand = NULL;
#endif

    // Dummy widgets for scrollbar corners:
    //layout->addWidget(new QWidget(this), 1, 1);
    //QWidget* w = new QWidget(this);
    //w->setEraseColor(QColor(255,0,0));
    gridStatus = new QLabel("-", this);
    gridStatus->setAlignment(Qt::AlignRight);
    layout->addMultiCellWidget(gridStatus, 1, 1, 1, 2);
    layout->addColSpacing(1, 50);

	
    setMouseTracking(true);
	// flickering under win:
    //setFocusPolicy(WheelFocus);
	
    setFocusPolicy(Qt::NoFocus);
}



/**
 * Destructor
 */
WF_GraphicView::~WF_GraphicView() {
    cleanUp();
	delete PixmapLayer1;
	delete PixmapLayer2;
	delete PixmapLayer3;
}



/**
 * @return width of widget.
 */
int WF_GraphicView::getWidth() {
    return width() - vScrollBar->sizeHint().width();
}



/**
 * @return height of widget.
 */
int WF_GraphicView::getHeight() {
    return height() - hScrollBar->sizeHint().height();
}


/**
 * Changes the current background color of this view.
 */
void WF_GraphicView::setBackground(const PF_Color& bg) {
    PF_GraphicView::setBackground(bg);

    setBackgroundColor(bg);
}



/**
 * Sets the mouse cursor to the given type.
 */
void WF_GraphicView::setMouseCursor(LX_Define::CursorType c) {

    switch (c) {
    default:
    case LX_Define::ArrowCursor:
        setCursor(Qt::ArrowCursor);
        break;
    case LX_Define::UpArrowCursor:
        setCursor(Qt::UpArrowCursor);
        break;
    case LX_Define::CrossCursor:
        setCursor(Qt::CrossCursor);
        break;
    case LX_Define::WaitCursor:
        setCursor(Qt::WaitCursor);
        break;
    case LX_Define::IbeamCursor:
        setCursor(Qt::IBeamCursor);
        break;
    case LX_Define::SizeVerCursor:
        setCursor(Qt::SizeVerCursor);
        break;
    case LX_Define::SizeHorCursor:
        setCursor(Qt::SizeHorCursor);
        break;
    case LX_Define::SizeBDiagCursor:
        setCursor(Qt::SizeBDiagCursor);
        break;
    case LX_Define::SizeFDiagCursor:
        setCursor(Qt::SizeFDiagCursor);
        break;
    case LX_Define::SizeAllCursor:
        setCursor(Qt::SizeAllCursor);
        break;
    case LX_Define::BlankCursor:
        setCursor(Qt::BlankCursor);
        break;
    case LX_Define::SplitVCursor:
        setCursor(Qt::SplitVCursor);
        break;
    case LX_Define::SplitHCursor:
        setCursor(Qt::SplitHCursor);
        break;
    case LX_Define::PointingHandCursor:
        setCursor(Qt::PointingHandCursor);
        break;
    case LX_Define::ForbiddenCursor:
        setCursor(Qt::ForbiddenCursor);
        break;
    case LX_Define::WhatsThisCursor:
        setCursor(Qt::WhatsThisCursor);
        break;

#ifndef __APPLE__

    case LX_Define::CadCursor:
        setCursor(*curCad);
        break;
    case LX_Define::DelCursor:
        setCursor(*curDel);
        break;
    case LX_Define::SelectCursor:
        setCursor(*curSelect);
        break;
    case LX_Define::MagnifierCursor:
        setCursor(*curMagnifier);
        break;
    case LX_Define::MovingHandCursor:
        setCursor(*curHand);
        break;
#else
        // Reduced cursor selection for the Mac:
    case LX_Define::CadCursor:
        setCursor(Qt::CrossCursor);
        break;
    case LX_Define::DelCursor:
        setCursor(Qt::CrossCursor);
        break;
    case LX_Define::SelectCursor:
        setCursor(Qt::CrossCursor);
        break;
    case LX_Define::MagnifierCursor:
        setCursor(Qt::CrossCursor);
        break;
    case LX_Define::MovingHandCursor:
        setCursor(Qt::PointingHandCursor);
        break;
#endif
    }

}



/**
 * Sets the text for the grid status widget in the left bottom corner.
 */
void WF_GraphicView::updateGridStatusWidget(const PF_String& text) {
    gridStatus->setText(text);
}



/**
 * Redraws the widget.
 */
void WF_GraphicView::redraw(LX_Define::RedrawMethod method) { 
	redrawMethod=(LX_Define::RedrawMethod ) (redrawMethod | method);
	update(); // Paint when reeady to pain
//	repaint(); //Paint immediate
}



void WF_GraphicView::resizeEvent(QResizeEvent* /*e*/) {
    PF_DEBUG->print("WF_GraphicView::resizeEvent begin");
    adjustOffsetControls();
    adjustZoomControls();
//     updateGrid();
	// Small hack, delete teh snapper during resizes
	getOverlayContainer(LX_Define::Snapper)->clear();
	redraw();
    PF_DEBUG->print("WF_GraphicView::resizeEvent end");
}



void WF_GraphicView::emulateMouseMoveEvent() {
    PF_MouseEvent e(QEvent::MouseMove, QPoint(mx, my),
                    Qt::NoButton, Qt::NoButton);
    //mouseMoveEvent(&e);
}



void WF_GraphicView::mousePressEvent(QMouseEvent* e) {
    // pan zoom with middle mouse button
#if QT_VERSION < 0x040700
    if (e->button()==Qt::MidButton /*|| (e->state()==Qt::LeftButton|Qt::AltButton)*/) {
#else
    if (e->button()==Qt::MiddleButton /*|| (e->state()==Qt::LeftButton|Qt::AltButton)*/) {
#endif
        setCurrentAction(new WF_ZoomPan(*container, *this));
    }

    PF_GraphicView::mousePressEvent(e);
    QWidget::mousePressEvent(e);
}


void WF_GraphicView::mouseReleaseEvent(QMouseEvent* e) {
	PF_DEBUG->print("WF_GraphicView::mouseReleaseEvent");
    PF_GraphicView::mouseReleaseEvent(e);
    //QWidget::mouseReleaseEvent(e);

    if (!e->isAccepted()) {
        if (QG_DIALOGFACTORY!=NULL && QG_DIALOGFACTORY->getCadToolBar()!=NULL) {
			PF_DEBUG->print("WF_GraphicView::mouseReleaseEvent: "
				"fwd to cadtoolbar");
            QG_DIALOGFACTORY->getCadToolBar()->mouseReleaseEvent(e);
        }
    }
	PF_DEBUG->print("WF_GraphicView::mouseReleaseEvent: OK");
}


void WF_GraphicView::mouseMoveEvent(QMouseEvent* e) {
    //PF_DEBUG->print("WF_GraphicView::mouseMoveEvent begin");
    //PF_MouseEvent rsm = QG_Qt2Rs::mouseEvent(e);

    PF_GraphicView::mouseMoveEvent(e);
    QWidget::mouseMoveEvent(e);

#ifdef Q_OS_WIN32
	// make sure that we can still use hotkeys and the mouse wheel
	if (parent()!=NULL) {
		((QWidget*)parent())->setFocus();
	}
#endif
	
    //PF_DEBUG->print("WF_GraphicView::mouseMoveEvent end");
}


/**
 * support for the wacom graphic tablet.
 */
void WF_GraphicView::tabletEvent(QTabletEvent* e) {
    if (hasMouse()) {
        switch (e->device()) {
        case QTabletEvent::Eraser:
            if (e->type()==QEvent::TabletRelease) {
                if (container!=NULL) {

                    WF_SelectSingle* a =
                        new WF_SelectSingle(*container, *this);
                    setCurrentAction(a);
                    QMouseEvent ev(QEvent::MouseButtonRelease, e->pos(),
                                   Qt::LeftButton, Qt::LeftButton);
                    mouseReleaseEvent(&ev);
                    a->finish();

                    if (container->countSelected()>0) {
                        setCurrentAction(
                            new WF_ModifyDelete(*container, *this));
                    }
                }
            }
            break;

        case QTabletEvent::Stylus:
        case QTabletEvent::Puck:
            if (e->type()==QEvent::TabletPress) {
                QMouseEvent ev(QEvent::MouseButtonPress, e->pos(),
                               Qt::LeftButton, Qt::LeftButton);
                mousePressEvent(&ev);
            } else if (e->type()==QEvent::TabletRelease) {
                QMouseEvent ev(QEvent::MouseButtonRelease, e->pos(),
                               Qt::LeftButton, Qt::LeftButton);
                mouseReleaseEvent(&ev);
            } else if (e->type()==QEvent::TabletMove) {
                QMouseEvent ev(QEvent::MouseMove, e->pos(),
                               Qt::NoButton, 0);
                mouseMoveEvent(&ev);
            }
            break;

        default:
            break;
        }
    }

    // a 'mouse' click:
    /*if (e->pressure()>10 && lastPressure<10) {
    	QMouseEvent e(QEvent::MouseButtonPress, e->pos(), 
    	   Qt::LeftButton, Qt::LeftButton);
    	mousePressEvent(&e);
}
    else if (e->pressure()<10 && lastPressure>10) {
    	QMouseEvent e(QEvent::MouseButtonRelease, e->pos(), 
    	   Qt::LeftButton, Qt::LeftButton);
    	mouseReleaseEvent(&e);
}	else if (lastPos!=e->pos()) {
    	QMouseEvent e(QEvent::MouseMove, e->pos(), 
    	   Qt::NoButton, 0);
    	mouseMoveEvent(&e);
}

    lastPressure = e->pressure();
    lastPos = e->pos();
    */
}

void WF_GraphicView::leaveEvent(QEvent* e) {
    PF_GraphicView::mouseLeaveEvent();
    QWidget::leaveEvent(e);
}


void WF_GraphicView::enterEvent(QEvent* e) {
    PF_GraphicView::mouseEnterEvent();
    QWidget::enterEvent(e);
}


void WF_GraphicView::focusOutEvent(QFocusEvent* e) {
    QWidget::focusOutEvent(e);
}


void WF_GraphicView::focusInEvent(QFocusEvent* e) {
    PF_GraphicView::mouseEnterEvent();
    QWidget::focusInEvent(e);
}


/**
 * mouse wheel event. zooms in/out or scrolls when
 * shift or ctrl is pressed.
 */
void WF_GraphicView::wheelEvent(QWheelEvent *e) {
    //PF_DEBUG->print("wheel: %d", e->delta());

    //printf("state: %d\n", e->state());
    //printf("ctrl: %d\n", Qt::ControlButton);

    if (container==NULL) {
        return;
    }

	PF_Vector mouse = toGraph(PF_Vector(e->x(), e->y()));

    bool scroll = false;
    LX_Define::Direction direction = LX_Define::Up;

    // scroll up / down:
    if (e->state()==Qt::ControlModifier) {
        scroll = true;
        if (e->delta()>0) {
            direction = LX_Define::Up;
        } else {
            direction = LX_Define::Down;
        }
    }

    // scroll left / right:
    else if	(e->state()==Qt::ShiftModifier) {
        scroll = true;
        if (e->delta()>0) {
            direction = LX_Define::Right;
        } else {
            direction = LX_Define::Left;
        }
    }

    if (scroll) {
        setCurrentAction(new WF_ZoomScroll(direction,
                         *container, *this));
    }

    // zoom in / out:
    else if (e->state()==0) {
        if (e->delta()>0) {
            setCurrentAction(new WF_ZoomIn(*container, *this,
                                                 LX_Define::In, LX_Define::Both,
												 mouse));
        } else {
            setCurrentAction(new WF_ZoomIn(*container, *this,
                                                 LX_Define::Out, LX_Define::Both,
												 mouse));
        }
    }
	
	redraw();

    e->accept();
}


void WF_GraphicView::keyPressEvent(QKeyEvent* e) {
    //if (e->key()==Qt::Key_Control) {
    //	setCtrlPressed(true);
    //}


    if (container==NULL) {
        return;
    }

    bool scroll = false;
    LX_Define::Direction direction = LX_Define::Up;

    switch (e->key()) {
    case Qt::Key_Left:
        scroll = true;
        direction = LX_Define::Right;
        break;
    case Qt::Key_Right:
        scroll = true;
        direction = LX_Define::Left;
        break;
    case Qt::Key_Up:
        scroll = true;
        direction = LX_Define::Up;
        break;
    case Qt::Key_Down:
        scroll = true;
        direction = LX_Define::Down;
        break;
    default:
        scroll = false;
        break;
    }

    if (scroll) {
        setCurrentAction(new WF_ZoomScroll(direction,
                         *container, *this));
    }

    PF_GraphicView::keyPressEvent(e);
}


void WF_GraphicView::keyReleaseEvent(QKeyEvent* e) {
    //if (e->key()==Qt::Key_Control) {
    //	setCtrlPressed(false);
    //}
    PF_GraphicView::keyReleaseEvent(e);
}


/**
 * Called whenever the graphic view has changed.
 * Adjusts the scrollbar ranges / steps.
 */
void WF_GraphicView::adjustOffsetControls() {
	static bool running = false;

	if (running) {
		return;
	}

	running = true;

    PF_DEBUG->print("WF_GraphicView::adjustOffsetControls() begin");

    if (container==NULL || hScrollBar==NULL || vScrollBar==NULL) {
        return;
    }

    int ox = getOffsetX();
    int oy = getOffsetY();
	
    PF_Vector min = container->getMin();
    PF_Vector max = container->getMax();

    // no drawing yet - still allow to scroll
    if (max.x < min.x+1.0e-6 || 
	    max.y < min.y+1.0e-6 ||
		max.x > LX_DOUBLEMAX || 
		max.x < LX_DOUBLEMIN || 
		min.x > LX_DOUBLEMAX || 
		min.x < LX_DOUBLEMIN ||
		max.y > LX_DOUBLEMAX || 
		max.y < LX_DOUBLEMIN || 
		min.y > LX_DOUBLEMAX || 
		min.y < LX_DOUBLEMIN ) {
        min = PF_Vector(-10,-10);
        max = PF_Vector(100,100);
    }
	
	int minVal = (int)(min.x * getFactor().x 
			- QG_SCROLLMARGIN - getBorderLeft());
	int maxVal = (int)(max.x * getFactor().x 
			- getWidth() + QG_SCROLLMARGIN + getBorderRight());

	hScrollBar->setValue(0);
	if (minVal<=maxVal) {
		hScrollBar->setRange(minVal, maxVal);
	}
    //hScrollBar->setMinValue(minVal);
    
	//hScrollBar->setMaxValue(maxVal);

	minVal = (int)(getHeight() - max.y * getFactor().y 
			- QG_SCROLLMARGIN - getBorderTop());
	maxVal = (int)(QG_SCROLLMARGIN + getBorderBottom() 
			- (min.y * getFactor().y));

	if (minVal<=maxVal) {
		vScrollBar->setRange(minVal, maxVal);
	}
    //vScrollBar->setMaxValue((int)(QG_SCROLLMARGIN + getBorderBottom()
     //                             - (min.y * getFactor().y)));
								  
	
    //vScrollBar->setMinValue((int)(getHeight() -
     //                             max.y * getFactor().y
     //                             - QG_SCROLLMARGIN - getBorderTop()));
								  

    hScrollBar->setPageStep((int)(getWidth()));
    vScrollBar->setPageStep((int)(getHeight()));

    hScrollBar->setValue(-ox);
    vScrollBar->setValue(oy);
	

    slotHScrolled(-ox);
    slotVScrolled(oy);
	

    PF_DEBUG->print("H min: %d / max: %d / step: %d / value: %d\n",
                    hScrollBar->minValue(), hScrollBar->maxValue(),
                    hScrollBar->pageStep(), ox);
    PF_DEBUG->print("V min: %d / max: %d / step: %d / value: %d\n",
                    vScrollBar->minValue(), vScrollBar->maxValue(),
                    vScrollBar->pageStep(), oy);

    PF_DEBUG->print("WF_GraphicView::adjustOffsetControls() end");

	running = false;
}


/**
 * override this to adjust controls and widgets that
 * control the zoom factor of the graphic.
 */
void WF_GraphicView::adjustZoomControls() {}


/**
 * Slot for horizontal scroll events.
 */
void WF_GraphicView::slotHScrolled(int value) {
    // Scrollbar behaviour tends to change with every Qt version..
    // so let's keep old code in here for now

    //static int running = false;
    //if (!running) {
    //running = true;
    ////PF_DEBUG->print("value x: %d\n", value);
    if (hScrollBar->maxValue()==hScrollBar->minValue()) {
        centerOffsetX();
    } else {
        setOffsetX(-value);
    }
    //if (isUpdateEnabled()) {
//         updateGrid();
    redraw();
}


/**
 * Slot for vertical scroll events.
 */
void WF_GraphicView::slotVScrolled(int value) {
    // Scrollbar behaviour tends to change with every Qt version..
    // so let's keep old code in here for now

    //static int running = false;
    //if (!running) {
    //running = true;
    ////PF_DEBUG->print("value y: %d\n", value);
    if (vScrollBar->maxValue()==vScrollBar->minValue()) {
        centerOffsetY();
    } else {
        setOffsetY(value);
    }
    //if (isUpdateEnabled()) {
  //  updateGrid();
    redraw();
}

QPixmap* WF_GraphicView::getPixmapForView(QPixmap *pm)
{
	
	if (pm==NULL) {
		return new QPixmap(getWidth(), getHeight());
	} else if (pm->width()!=getWidth() || pm->height()!=getHeight()) {
		delete pm;
		return new QPixmap(getWidth(), getHeight());
	} else {
		return pm;
	}
}


/**
 * Handles paint events by redrawing the graphic in this view.
 * usually that's very fast since we only paint the buffer we
 * have from the last call..
 */
void WF_GraphicView::paintEvent(QPaintEvent *) {
    PF_DEBUG->print("WF_GraphicView::paintEvent begin");
	
	PF_SETTINGS->beginGroup("/Appearance");
    bool draftMode = (bool)PF_SETTINGS->readNumEntry("/DraftMode", 0);
    PF_SETTINGS->endGroup();

	
	// Re-Create or get the layering pixmaps
	PixmapLayer1=getPixmapForView(PixmapLayer1);
	PixmapLayer2=getPixmapForView(PixmapLayer2);
	PixmapLayer3=getPixmapForView(PixmapLayer3);

	// Draw Layer 1
	if (redrawMethod & LX_Define::RedrawGrid) {
		PixmapLayer1->fill(background);
		PF_PainterQt painter1(PixmapLayer1);
		//painter1->setBackgroundMode(Qt::OpaqueMode);
		//painter1->setBackgroundColor(background);
		//painter1->eraseRect(0,0,getWidth(), getHeight());
		drawLayer1((PF_Painter*)&painter1);
		painter1.end();
	}
	

	if (redrawMethod & LX_Define::RedrawDrawing) {
		// DRaw layer 2
		PixmapLayer2->fill(Qt::transparent);
		PF_PainterQt painter2(PixmapLayer2);
		painter2.setDrawingMode(drawingMode);
		setDraftMode(draftMode);
		drawLayer2((PF_Painter*)&painter2);
		setDraftMode(false);
		painter2.end();
	}
	
	if (redrawMethod & LX_Define::RedrawOverlay) {
		PixmapLayer3->fill(Qt::transparent);
		PF_PainterQt painter3(PixmapLayer3);
		drawLayer3((PF_Painter*)&painter3);
		painter3.end();
	} 
		
	// Finally paint the layers back on the screen, bitblk to the rescue!
	PF_PainterQt wPainter(this);
	//wPainter.setCompositionMode(QPainter::CompositionMode_Screen);
	wPainter.drawPixmap(0,0,*PixmapLayer1);
	wPainter.drawPixmap(0,0,*PixmapLayer2);
	wPainter.drawPixmap(0,0,*PixmapLayer3);
	wPainter.end();
	
	redrawMethod=LX_Define::RedrawNone;
    PF_DEBUG->print("WF_GraphicView::paintEvent end");
}



/**
 * Previews the given url for the file open dialog.
 */
void WF_GraphicView::previewUrl(const Q3Url &u) {
	//static PF_Graphic* gr = new PF_Graphic();
	// TODO: Decide if previewurl is needed in qt4
	/*
	PF_DEBUG->print("WF_GraphicView::previewUrl");
	
	if (container!=NULL && container->rtti()==LX_Define::EntityGraphic) {
		((PF_Graphic*)container)->open(u.path(), LX_Define::FormatUnknown);
		zoomAuto();
	}
	//setContainer(gr);
	
	PF_DEBUG->print("WF_GraphicView::previewUrl: OK");
	 */
}


